package cn.com.bluemoon.daps.sync.core.sync.sql;

import cn.com.bluemoon.daps.common.sql.layer.ILayerCheck;
import cn.com.bluemoon.daps.common.sql.layer.LayerCheckFactory;
import cn.com.bluemoon.daps.model.entity.DapDataModelSubordinateKey;
import cn.com.bluemoon.daps.model.entity.DapDataModelTable;
import cn.com.bluemoon.daps.model.entity.DapDataModelTableField;
import cn.com.bluemoon.daps.model.entity.DapDataModelTablePartition;
import cn.com.bluemoon.daps.sync.base.BmScript;
import cn.com.bluemoon.daps.sync.core.sync.sql.hive.HiveBmSql;
import cn.com.bluemoon.daps.sync.core.sync.sql.mysql.MysqlBmSql;
import cn.com.bluemoon.daps.sync.core.sync.sql.postgresql.PgSqlBmSql;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Jarod Kong
 * @date 2021-01-16 00:17
 **/
@Slf4j
public class BmSql implements ITableSql, IFieldSql, IIndexSql, IPartitionSql {
    private final AbstractSql abstractSql;
    private final DbType dbType;
    protected boolean openLayerCheck;

    protected BmSql(AbstractSql abstractSql) {
        this.abstractSql = abstractSql;
        this.dbType = abstractSql.dbType;
    }

    public static BmSql use(DbType dbType) {
        AbstractSql abstractSql;
        if (DbType.postgresql.equals(dbType)) {
            abstractSql = new PgSqlBmSql();
        } else if (DbType.hive.equals(dbType)) {
            abstractSql = new HiveBmSql();
        } else {
            abstractSql = new MysqlBmSql();
        }
        return use(abstractSql);
    }

    public static BmSql use() {
        AbstractSql mysqlBmSql = new MysqlBmSql();
        return new BmSql(mysqlBmSql);
    }

    public static BmSql use(AbstractSql abstractSql) {
        return new BmSql(abstractSql);
    }

    @VisibleForTesting
    public static void main(String[] args) {
        List<String> collect = Stream.
                of("alter table `oa_remake_seal_apply_details`  change column `out_detail_id` `out_detail_id`  varchar(32)  unsigned  default '' comment '外部系统的用印信息ID'  after remark", "alter table `oa_remake_seal_apply_details`  change column `out_detail_id` `out_detail_id`  varchar(32)  unsigned  default '' comment '外部系统的用印信息ID'  after remark")
                .map(s -> {
//                    return SQLUtils.format(s, DbType.mysql);
                    ILayerCheck check = LayerCheckFactory.type();
                    SQLUtils.FormatOption formatOption = new SQLUtils.FormatOption();
                    formatOption.setPrettyFormat(true);
                    formatOption.setUppCase(false);
                    return check.getFormatSql(s, DbType.mysql, formatOption);
                }).collect(Collectors.toList());
        System.out.println(collect);
    }

    public BmSql openLayerCheck() {
        this.openLayerCheck = true;
        log.debug("开启SQL检查+格式化");
        return this;
    }

    @Override
    public BmScript createField(DapDataModelTable newT, DapDataModelTableField newF, String preFieldName) {
        BmScript sql = abstractSql.createField(newT, newF, preFieldName);
        return checkSqlLeyer(sql);
    }

    @Override
    public BmScript dropField(DapDataModelTable newT, DapDataModelTableField oldF) {
        BmScript sql = abstractSql.dropField(newT, oldF);
        return checkSqlLeyer(sql);
    }

    @Override
    public BmScript changeField(DapDataModelTable newT, DapDataModelTableField oldF, DapDataModelTableField newF, String preFieldName) {
        BmScript sql = abstractSql.changeField(newT, oldF, newF, preFieldName);
        return checkSqlLeyer(sql);
    }

    @Override
    public BmScript createIndex(DapDataModelTable newT, DapDataModelSubordinateKey newI) {
        BmScript sql = abstractSql.createIndex(newT, newI);
        return checkSqlLeyer(sql);
    }

    @Override
    public BmScript dropIndex(DapDataModelTable oldT, DapDataModelSubordinateKey oldI) {
        BmScript sql = abstractSql.dropIndex(oldT, oldI);
        return checkSqlLeyer(sql);
    }

    @Override
    public BmScript changeIndex(DapDataModelTable newT, DapDataModelSubordinateKey oldI, DapDataModelSubordinateKey newI) {
        BmScript sql = abstractSql.changeIndex(newT, oldI, newI);
        return checkSqlLeyer(sql);
    }

    @Override
    public BmScript createPartition(DapDataModelTable newT, DapDataModelTablePartition newP) {
        BmScript sql = abstractSql.createPartition(newT, newP);
        return checkSqlLeyer(sql);
    }

    @Override
    public BmScript dropPartition(DapDataModelTable oldT, DapDataModelTablePartition oldP) {
        BmScript sql = abstractSql.dropPartition(oldT, oldP);
        return checkSqlLeyer(sql);
    }

    @Override
    public BmScript changePartition(DapDataModelTable newT, DapDataModelTablePartition oldP, DapDataModelTablePartition newP) {
        BmScript sql = abstractSql.changePartition(newT, oldP, newP);
        return checkSqlLeyer(sql);
    }

    @Override
    public BmScript dropTable(String oldTableName) {
        BmScript sql = abstractSql.dropTable(oldTableName);
        return checkSqlLeyer(sql);
    }

    public BmScript checkSqlLeyer(BmScript sql) {
        if (openLayerCheck) {
            List<String> formatSql = sql.getSqls().stream().map(s -> {
                return SQLUtils.format(s, dbType);
            }).collect(Collectors.toList());
            sql.setSqls(formatSql);
            return sql;
        }
        return sql;
    }

    @Override
    public BmScript createTable(DapDataModelTable newT,
                                List<DapDataModelTableField> newFs,
                                List<DapDataModelSubordinateKey> newIs,
                                List<DapDataModelTablePartition> newPs) {
        BmScript sql = abstractSql.createTable(newT, newFs, newIs, newPs);
        return checkSqlLeyer(sql);
    }

    @Override
    public BmScript changeTable(DapDataModelTable oldT, DapDataModelTable newT) {
        BmScript sql = abstractSql.changeTable(oldT, newT);
        return checkSqlLeyer(sql);
    }

    /**
     * addBlackQuote 为增加 `
     * 默认则为true
     *
     * @return this
     */
    public BmSql noBlackQuote() {
        abstractSql.setAddBlackQuote(false);
        return this;
    }
}
